/*
 * Copyright (c) 2007, Tim Stack
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Tim Stack nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY TIM STACK ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL TIM STACK BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @file reflecxx.h
 */

#ifndef __reflecxx_h
#define __reflecxx_h

#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Enumeration of primitive types.  Note: The types that are given a specific
 * value, should match the same type/value in C/Invoke.
 */
typedef enum {
    RT_UNKNOWN = -1,
    RT_CHAR = 0,
    RT_SHORT = 1,
    RT_INT = 2,
    RT_LONG = 3,
    RT_LONG_LONG = 4,
    RT_FLOAT = 5,
    RT_DOUBLE = 6,
    RT_POINTER = 7,
    RT_UNSIGNED_CHAR,
    RT_UNSIGNED_INT,
    RT_UNSIGNED_SHORT,
    RT_UNSIGNED_LONG,
    RT_UNSIGNED_LONG_LONG,
    RT_BOOL,
    RT_VOID,
    RT_STRING,
    RT_ARRAY,
    RT_ENUM,
    RT_STRUCT,
    RT_UNION,
    RT_BIT_FIELD,
    RT_FUNCTION,

    RT__MAX
} reflecxx_type_t;

/**
 * Container for primitive types.  Primarily for use with the library
 * functions.
 */
typedef union {
    char rp_char;
    int rp_int;
    short rp_short;
    long rp_long;
    long long rp_long_long;
    unsigned char rp_unsigned_char;
    unsigned int rp_unsigned_int;
    unsigned short rp_unsigned_short;
    unsigned long rp_unsigned_long;
    unsigned long long rp_unsigned_long_long;
    float rp_float;
    double rp_double;
    char *rp_string;
    void *rp_pointer;
    int rp_enum;
} reflecxx_primitive_t;

struct reflecxx_array;
struct reflecxx_pointer;
struct reflecxx_enum;
struct reflecxx_struct;
struct reflecxx_function;

/**
 * Combination of a primitive type and any additional type information for
 * structures, enums, and pointers.
 */
struct reflecxx_full_type {
    reflecxx_type_t rft_type;
    union {
	/**
	 * Keep this as the first member so we can do static initialization
	 * without any errors/warnings.
	 */
	void *init;
	
	struct reflecxx_array *arr;
	struct reflecxx_pointer *ptr;
	struct reflecxx_enum *en;
	struct reflecxx_struct *st;
	struct reflecxx_function *fun;
    } rft_type_info;
};

/** Type information for an array. */
struct reflecxx_array {
    unsigned int ra_lo;
    unsigned int ra_hi;
    struct reflecxx_full_type ra_element_type;
};

/** Type information for the type a pointer refers to. */
struct reflecxx_pointer {
    struct reflecxx_full_type rp_full_type;
};

/** Type information for a field in a struct. */
struct reflecxx_field {
    const char *rf_name; /*< The name of the field. */
    struct reflecxx_full_type rf_full_type;
    unsigned int rf_bit_offset; /*< Bit offset of the field in the struct. */
    unsigned int rf_bit_length; /*< Bit length of the field in the struct. */
};

/** Type information for a struct. */
struct reflecxx_struct {
    int rs_byte_length; /*< The length of the data structure, in bytes. */
    int rs_field_count; /*< The number of fields in the structure. */
    struct reflecxx_field *rs_fields; /*< Pointer to the array of fields. */
};

/** Type information for an element in an enumeration. */
struct reflecxx_enum_element {
    const char *ree_name; /*< The enumerated value's symbol. */
    int ree_value;        /*< The enumerated value. */
};

/** Type information for an enum. */
struct reflecxx_enum {
    unsigned int re_element_count; /*< The number of elements in this enum. */
    struct reflecxx_enum_element *re_elements; /*< The array of elements. */
};

/** Type information for a function parameter. */
struct reflecxx_param {
    const char *rp_name;
    struct reflecxx_full_type rp_full_type;
};

/** Type information for a function. */
struct reflecxx_function {
    struct reflecxx_full_type rf_return_type;
    unsigned int rf_parameter_count;
    struct reflecxx_param *rf_parameters;
    const char *rf_cinv_return;
    const char *rf_cinv_parameters;
};

/** Pointer to the malloc reflecxx should use, defaults to stdlib. */
extern void *(*reflecxx_malloc)(size_t);

/** Pointer to the free function reflecxx should use, defaults to stdlib. */
extern void (*reflecxx_free)(void *);

/** Pointer to the realloc function reflecxx should use, defaults to stdlib. */
extern void *(*reflecxx_realloc)(void *, size_t);

/**
 * @param rft The type to get the sizeof.
 * @return The size of the type in bytes.
 */
size_t reflecxx_sizeof(struct reflecxx_full_type *rft);

/**
 * Enum element that is used returned when a match could not by found by
 * reflecxx_enum_by_name or reflecxx_enum_by_value.
 */
extern struct reflecxx_enum_element UNKNOWN_ENUM_ELEMENT;

/**
 * Find an enum element by name.
 *
 * @param rft The enum type to query.
 * @param name The name of the enum value.
 * @param name_len The length of the name string or -1 if it's null terminated.
 * @return The enum element found in 'rft' or UNKNOWN_ENUM_ELEMENT if a match
 * could not be found.
 */
struct reflecxx_enum_element *
reflecxx_enum_by_name(struct reflecxx_full_type *rft,
		      const char *name,
		      int name_len);

/**
 * Find an enum element by its numeric value.
 *
 * @param rft The enum type to query.
 * @param value The numeric value to search for.
 * @return The enum element found in 'rft' or UNKNOWN_ENUM_ELEMENT if a match
 * could not be found.
 */
struct reflecxx_enum_element *
reflecxx_enum_by_value(struct reflecxx_full_type *rft, int value);

/**
 * Find a structure/union field by name.
 *
 * @param rft The struct/union to query.
 * @param name The name of the enum value.
 * @param name_len The length of the name string or -1 if it's null terminated.
 * @return The field with the given name or NULL if no match was found.
 */
struct reflecxx_field *
reflecxx_field_by_name(struct reflecxx_full_type *rft,
		       const char *name,
		       int name_len);

/**
 * Transform the given string into a JSON-escaped string.  For example, passing
 * the string '\tHello, World\n' will yield '\"\\tHello, World\\n\"'.
 *
 * @param str The string to transform.
 * @return The JSON-escaped version of the string that was allocated with
 * reflecxx_alloc, or NULL if there was an error.
 */
char *reflecxx_escape(const char *str);

/**
 * Transform a JSON-escaped string into a regular string.
 *
 * @param str The JSON-escaped string.
 * @param length_out On return, this will be set to the number of characters
 * consumed.
 * @return The unescaped string, allocated with reflecxx_alloc.
 */
char *reflecxx_unescape(const char *str, unsigned int *length_out);

enum {
    RBB_RESIZABLE
};

enum {
    /** Flag that indicates the buffer is resizable using reflecxx_realloc. */
    RBF_RESIZABLE = (1L << RBB_RESIZABLE)
};

/** Buffer used for printing values. */
struct reflecxx_buffer {
    char *rb_data;
    off_t rb_offset;
    size_t rb_used;
    size_t rb_max_size;
    unsigned long rb_flags;
};

/** Error codes used by reflecxx_scan. */
typedef enum {
    REC_OK,
    REC_PARSE_ERROR,
    REC_UNKNOWN_FIELD,
    REC_MISSING_FIELD,
    REC_OUT_OF_MEMORY,
    REC_OUT_OF_RANGE
} reflecxx_error_code_t;

/** Description of errors encountered by reflecxx_scan. */
struct reflecxx_error {
    reflecxx_error_code_t re_code;
    unsigned int re_line; /*< The line number where the error occurred. */
    unsigned int re_column; /*< The column where the error occurred. */
    char re_msg[128]; /*< A descriptive message of the error. */
};

/** Convenience used to initialize a resizable reflecxx_buffer variable. */
extern const struct reflecxx_buffer REFLECXX_EMPTY_BUFFER;

/**
 * Convenience used to initialize a reflecxx_buffer from a static string.
 *
 * @param data The string to initialize the buffer with.
 * @return A reflecxx_buffer that refers to the given string.
 */
struct reflecxx_buffer reflecxx_buffer_string(char *data);

/**
 * Append a formatted string to a reflecxx_buffer and resize the buffer if
 * necessary.
 *
 * @param rb The buffer to append a string to.
 * @param size The amount of buffer space to reserve for the appended string.
 * @param format The printf-like string format.
 * @return True if the string was appended successfully.
 */
int rbappendf(struct reflecxx_buffer *rb, size_t size,
	      const char *format,
	      ...)
#if defined(__GNUC__)
     __attribute__ ((format(printf, 3, 4)))
#endif
     ;

/**
 * Print a value, using JSON-encoding, to the given buffer.
 *
 * @param dst The buffer to write to.
 * @param rft The type of the value.
 * @param rp The value to write.
 * @return True on success, false otherwise.
 */
int reflecxx_pprint(struct reflecxx_buffer *dst,
		    struct reflecxx_full_type *rft,
		    reflecxx_primitive_t rp);

/**
 * Print a value, using JSON-encoding, to the given buffer.
 *
 * @param dst The buffer to write to.
 * @param rft The type of the value.
 * @param args The raw value, on the stack, not stored in a
 * reflecxx_primitive_t.
 * @return True on success, false otherwise.
 */
int reflecxx_vprint(struct reflecxx_buffer *dst,
		    struct reflecxx_full_type *rft,
		    va_list args);

/**
 * Print a value, using JSON-encoding, and return it as a string.
 *
 * @param rft The type of the value.
 * @param args The raw value, on the stack, not stored in a
 * reflecxx_primitive_t.
 * @return The JSON-encoded value as a string allocated with reflecxx_alloc, or
 * NULL if there was an error.
 */
char *reflecxx_aprint(struct reflecxx_full_type *rft, ...);

/**
 * Print a value, using JSON-encoding, to the given buffer.
 *
 * @param dst The buffer to write to.
 * @param size The maximum size of the buffer.
 * @param rft The type of the value.
 * @param args The raw value, on the stack, not stored in a
 * reflecxx_primitive_t.
 * @return True on success, false otherwise.
 */
int reflecxx_print(char *buffer,
		   size_t size,
		   struct reflecxx_full_type *rft,
		   ...);

/**
 * Decode a JSON-encoded value.
 *
 * @param rb The string to decode.  On return, rb_offset will refer to 
 * @param rft The type of value.
 * @param rp_inout
 * @param re A reflecxx_error object to be filled out with additional
 * information describing the error.
 * @return True on success, false otherwise.
 */
int reflecxx_scan(struct reflecxx_buffer *rb,
		  struct reflecxx_full_type *rft,
		  reflecxx_primitive_t *rp_inout,
		  struct reflecxx_error *re);

/**
 * Get the value of a field in a structure.
 *
 * @param obj Pointer to the structure to retrieve a field from.
 * @param rf The type information for the field in the object.
 * @return A reflecxx_primitive_t containing the value.
 */
reflecxx_primitive_t reflecxx_field_get(void *obj, struct reflecxx_field *rf);

/**
 * Set the value of a field in a structure.
 *
 * @param obj Pointer to the structure whose field should be mutated.
 * @param rf The type information for the field in the object.
 * @param value A reflecxx_primitive_t containing the value to store.
 */
void reflecxx_field_set(void *obj,
			struct reflecxx_field *rf,
			reflecxx_primitive_t value);

/**
 * Get the value of an index in an array.
 *
 * @param obj Pointer to the array.
 * @param ra The type information for the array.
 * @param index The index of the element to get from the array.
 * @return The element's value.
 */
reflecxx_primitive_t reflecxx_array_get(void *obj,
					struct reflecxx_array *ra,
					unsigned int index);

/**
 * Set the value of an index in an array.
 *
 * @param obj Pointer to the array.
 * @param ra The type information for the array.
 * @param index The index of the element to get from the array.
 * @param value A reflecxx_primitive_t containing the value to store.
 */
void reflecxx_array_set(void *obj,
			struct reflecxx_array *ra,
			unsigned int index,
			reflecxx_primitive_t value);

#ifdef __cplusplus
}
#endif

#endif
